#pragma once

#include "safe_bool.h"
#include "nocopyable.h"

namespace gtl
{

template<class T>
class compare : public safe_bool<compare<typename T>>, nocopyable
{
	enum { great = 1, less, great_equal, less_equal, equal, not_equal };

public:
	compare(T val)
		: m_val(val)
		, m_op(true)
		, m_result(true)
		, m_cmp_op(0)
	{
	}

public:
	bool null()
	{
		bool ret = !m_result;
		m_op = true;
		m_result = m_op;
		return ret;
	}

public:
	compare& operator!()
	{
		m_op = !m_op;
		m_result = m_op;
		return *this;
	}

public:
	// >
	template<class T, class V>
	friend compare<T>& operator>(compare<T>& cmp, V val)
	{
		cmp.m_cmp_op = great;
		return cmp.assign(cmp.m_val > val);
	}

	template<class T, class V>
	friend compare<T>& operator>(V val, compare<T>& cmp)
	{
		cmp.m_cmp_op = great;
		return cmp.assign(val > cmp.m_val);
	}

	// <
	template<class T, class V>
	friend compare<T>& operator<(compare<T>& cmp, V val)
	{
		cmp.m_cmp_op = less;
		return cmp.assign(cmp.m_val < val);
	}

	template<class T, class V>
	friend compare<T>& operator<(V val, compare<T>& cmp)
	{
		cmp.m_cmp_op = less;
		return cmp.assign(val < cmp.m_val);
	}

	// >=
	template<class T, class V>
	friend compare<T>& operator>=(compare<T>& cmp, V val)
	{
		cmp.m_cmp_op = great_equal;
		return cmp.assign(cmp.m_val >= val);
	}

	template<class T, class V>
	friend compare<T>& operator>=(V val, compare<T>& cmp)
	{
		cmp.m_cmp_op = great_equal;
		return cmp.assign(val >= cmp.m_val);
	}

	// <=
	template<class T, class V>
	friend compare<T>& operator<=(compare<T>& cmp, V val)
	{
		cmp.m_cmp_op = less_equal;
		return cmp.assign(cmp.m_val <= val);
	}

	template<class T, class V>
	friend compare<T>& operator<=(V val, compare<T>& cmp)
	{
		cmp.m_cmp_op = less_equal;
		return cmp.assign(val <= cmp.m_val);
	}

	// ==
	template<class T, class V>
	friend compare<T>& operator==(compare<T>& cmp, V val)
	{
		cmp.m_cmp_op = equal;
		return cmp.assign(cmp.m_val == val);
	}

	template<class T, class V>
	friend compare<T>& operator==(V val, compare<T>& cmp)
	{
		cmp.m_cmp_op = equal;
		return cmp.assign(val == cmp.m_val);
	}

	// !=
	template<class T, class V>
	friend compare<T>& operator!=(compare<T>& cmp, V val)
	{
		cmp.m_cmp_op = not_equal;
		return cmp.assign(cmp.m_val != val);
	}

	template<class T, class V>
	friend compare<T>& operator!=(V val, compare<T>& cmp)
	{
		cmp.m_cmp_op = not_equal;
		return cmp.assign(val != cmp.m_val);
	}

	// |
	template<class T, class V>
	friend compare<T>& operator|(compare<T>& cmp, V val)
	{
		cmp.m_op = false;
		return cmp.assign(cmp.cmp(cmp.m_val, val));
	}

	// &
	template<class T, class V>
	friend compare<T>& operator&(compare<T>& cmp, V val)
	{
		cmp.m_op = true;
		return cmp.assign(cmp.cmp(cmp.m_val, val));
	}

private:
	compare& assign(bool ret)
	{
		m_result = m_op ? (m_result && ret) : (m_result || ret);
		return *this;
	}

	template<class U, class V>
	bool cmp(U u, V v)
	{
		switch(m_cmp_op)
		{
		case great:
			return u > v;

		case less:
			return u < v;

		case great_equal:
			return u >= v;

		case less_equal:
			return u <= v;

		case equal:
			return u == v;

		case not_equal:
			return u != v;

		default:
			return false;
		}
	}

private:
	T m_val;
	bool m_op;
	bool m_result;
	char m_cmp_op;
};

} // end of namespace gtl